package com.hzy.alibaba.service.impl;

import com.hzy.alibaba.config.ManagementParameter;
import com.hzy.alibaba.dao.GeneratorDao;
import com.hzy.alibaba.entity.Column;
import com.hzy.alibaba.entity.DomainBo;
import com.hzy.alibaba.entity.FieldVo;
import com.hzy.alibaba.entity.Table;
import com.hzy.alibaba.enums.MySqlDataType;
import com.hzy.alibaba.enums.MySqlKeyType;
import com.hzy.alibaba.service.GeneratorService;
import com.hzy.alibaba.utils.FieldUtils;
import com.hzy.alibaba.utils.FreeMarkerUtils;
import com.hzy.alibaba.utils.TableUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class GeneratorServiceImpl implements GeneratorService {
    private static List<DomainBo> domainBos = new ArrayList<>();

    @Value(value = "${parameter.databaseName}")
    private String databaseName;
    //表名固定前缀
    @Value(value = "${parameter.tablePrefix}")
    private String tablePrefix;
    //表名固定后缀
    @Value(value = "${parameter.tableSuffix}")
    private String tableSuffix;
    @Value(value = "${parameter.author}")
    private String author;
    @Autowired
    ManagementParameter dataParameter;

    @Autowired
    private GeneratorDao databasesDao;

    @Override
    public void initTableInfo() {
        List<Table> tables = databasesDao.getTables(databaseName);
        domainBos = new ArrayList<DomainBo>(tables.size());
        for (Table item : tables) {
            if (StringUtils.isNotBlank(tablePrefix) && item.getTableName().indexOf(tablePrefix) < 0) {
                continue;
            }
            List<Column> columns = databasesDao.getColumns(databaseName, item.getTableName());
            // 构建domain对象信息
            DomainBo domain = initDomain(columns);
            domain.setName(item.getTableName());
            domain.setComment(item.getTableComment());
            domain.setClassName(TableUtils.getClassName(item.getTableName(), StringUtils.isNotBlank(tablePrefix) ? tablePrefix : null, StringUtils.isNotBlank(tableSuffix) ? tableSuffix : null, true));
            domainBos.add(domain);
        }
    }

    @Override
    public void startManagementCreate() throws Exception {
        //初始化
        if(domainBos.size() < 1){
            initTableInfo();
        }

        for (DomainBo temp : domainBos) {
            temp.setPackageName(dataParameter.getDomianPackageName());
            temp.setAuthor(author);
            temp.setDatetime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

            buildManagement(temp, "management_domain.ftl", false);
            if (CollectionUtils.isNotEmpty(temp.getKeys()) && temp.getKeys().size() > 1) {
                buildManagement(temp, "management_domainKey.ftl", true);
            }
            buildManagementMapperXml(temp,"management_mapperXml.ftl");

            buildManagementMapper(temp,"management_mapper.ftl");

            buildManagementController(temp, "management_controller.ftl");

            buildManagementService(temp, "management_service.ftl");

            buildManagementServiceImpl(temp, "management_serviceImpl.ftl");
        }
    }

    /**
     * 根据表列字段生成原型构成对象
     *
     * @param columns 列集合
     * @return
     */
    private DomainBo initDomain(List<Column> columns) {
        List<FieldVo> keys = new ArrayList<FieldVo>();
        List<FieldVo> fields = new ArrayList<FieldVo>();
        Set<String> keyImports = new HashSet<String>();
        Set<String> imports = new HashSet<String>();
        for (Column item : columns) {
            FieldVo field = new FieldVo();
            field.setColName(item.getColumnName());
            field.setExtra(item.getExtra());
            field.setName(FieldUtils.getFieldName(item.getColumnName(), "_"));
            field.setComment(item.getColumnComment());
            // 获取列的JAVA属性
            MySqlDataType type = FieldUtils.getFieldTypeByMysql(item.getDataType());
            if (type != null) {
                String packageName = null;
                String javaType;
                // 判断字段是否是无符号类型
                if (item.getColumnType().contains("unsigned")) {
                    if (StringUtils.isNotBlank(type.getUnsignedPackageName())) {
                        packageName = type.getUnsignedPackageName();
                    }
                    javaType = type.getUnsignedType();
                } else {
                    if (StringUtils.isNotBlank(type.getPackageName())) {
                        packageName = type.getPackageName();
                    }
                    javaType = type.getJavaType();
                }
                field.setColType(type.getJdbcType());
                // 设置JAVA类型
                field.setType(javaType);
                // 判断是否是主键
                if (item.getColumnKey().toUpperCase().equals(MySqlKeyType.PRI.getValue())) {
                    if (StringUtils.isNotBlank(packageName)) {
                        keyImports.add(packageName);
                    }
                    keys.add(field);
                } else {
                    if (StringUtils.isNotBlank(packageName)) {
                        imports.add(packageName);
                    }
                    fields.add(field);
                }
            }
        }
        DomainBo domain = new DomainBo();
        // 设置主键
        domain.setKeys(CollectionUtils.isEmpty(keys) ? null : keys);
        // 设置主键引用
        domain.setKeyImports(CollectionUtils.isEmpty(keyImports) ? null : keyImports);
        // 设置列字段
        domain.setColumns(CollectionUtils.isEmpty(fields) ? null : fields);
        // 设置引用
        if (CollectionUtils.isEmpty(keys) && keys.size() == 1 &&  CollectionUtils.isEmpty(keyImports)) {
            imports.addAll(keyImports);
        }
        domain.setImports(CollectionUtils.isEmpty(imports) ? null : imports);
        return domain;
    }

    @Override
    public void buildManagement(DomainBo domain, String templateName, Boolean isKey) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = dataParameter.getBasePath() + "/" + dataParameter.getDataModule() + "/" + dataParameter.getDomianPath();

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + (isKey ? domain.getClassName() + "Key" : domain.getClassName()) + ".java");
            if(file.exists()){
                return;
            }

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void buildManagementMapperXml(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = dataParameter.getBasePath() + "/" + dataParameter.getDataModule() + "/" + dataParameter.getMapperXmlPath();

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + ".xml");
            if(file.exists()){
                return;
            }

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("mapperPackageName", dataParameter.getMapperPackageName());
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void buildManagementMapper(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = dataParameter.getBasePath() + "/" + dataParameter.getDataModule() + "/" + dataParameter.getMapperPath();

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "Mapper.java");
            if(file.exists()){
                return;
            }

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("mapperPackageName", dataParameter.getMapperPackageName());
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void buildManagementService(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = dataParameter.getBasePath() + "/" + dataParameter.getDataModule() + "/" + dataParameter.getServicePath();

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "Service.java");
            if(file.exists()){
                return;
            }

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("servicePackageName", dataParameter.getServicePackageName());
            params.put("domianPackageName", dataParameter.getDomianPackageName());
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void buildManagementServiceImpl(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = dataParameter.getBasePath() + "/" + dataParameter.getDataModule() + "/" + dataParameter.getServiceImplPath();

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "ServiceImpl.java");
            if(file.exists()){
                return;
            }

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("mapperPackageName", dataParameter.getMapperPackageName());
            params.put("domianPackageName", dataParameter.getDomianPackageName());
            params.put("servicePackageName", dataParameter.getServicePackageName());
            params.put("serviceImplPackageName", dataParameter.getServiceImplPackageName());
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void buildManagementController(DomainBo domain, String templateName) {
        try {
            Configuration cfg = FreeMarkerUtils.getConfiguration();
            cfg.setDirectoryForTemplateLoading(getTemplateDir());
            /* 在整个应用的生命周期中，这个工作你可以执行多次 */
            /* 获取或创建模板*/
            Template temp = cfg.getTemplate(templateName, "UTF-8");

            String path = dataParameter.getBasePath() + "/" + dataParameter.getDispatcherModule() + "/" + dataParameter.getControllerPath();

            File pathFile = new File(path);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }

            File file = new File(path + "/" + domain.getClassName() + "Controller.java");
            if(file.exists()){
                return;
            }

            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));

            /* 将模板和数据模型合并 */
            //Writer out = new OutputStreamWriter(System.out);
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("controllerPackageName", dataParameter.getControllerPackageName());
            params.put("servicePackageName", dataParameter.getServicePackageName());
            params.put("domianPackageName", dataParameter.getDomianPackageName());
            params.put("domain", domain);
            temp.process(params, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private File getTemplateDir() throws Exception {
        return ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "templates");
    }
}
